﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Shapes;
using ImTools;
using IntoTheVoid.Models;
using IntoTheVoid.Units;
using Microsoft.VisualBasic.ApplicationServices;
using Newtonsoft.Json;

namespace IntoTheVoid.State
{
	public class UpdateDiffJsonFile
	{
		public List<Info> addedFiles;
		public List<Info> updatedFiles;
		public List<Info> deletedFiles;
	}
	public class Info
	{
		public string name;
		public string md5;
		public string diff;
	}

	public class Md5Core
	{
		public Md5Core(string p, string m)
		{
			path = p;
			md5 = m;
		}
		public string path { get; set; }
		public string md5 { get; set; }
	}

	public class FileCheckState : State
	{
		static UpdateDiffJsonFile infos;
		string fileDiff;
		// 新增进度更新定时器
		private System.Timers.Timer _progressTimer;
		private int _processedCount;
		private int _totalFiles;
		public FileCheckState(StateContext context, string path = "")
		{
			fileDiff = path;
			buttonContent = "文件校验";
			buttonEnabla = false;
			Context = context;
			Context.OnButtonStateArgs(new ButtonStateChangeEventArgs(buttonContent, buttonEnabla, true, false, "StartDownloadButtonGray"));
			Start(context);
		}
		public static void FileCheckDeleteFile(string filePath)
		{
			if (!File.Exists(filePath))
				return;

			ReadCompareDiffListJson(filePath);

			if (infos == null || infos.deletedFiles == null)
				return;

			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var downloadPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			downloadPath = System.IO.Path.Combine(downloadPath, "IntoTheVoid");
			foreach (var i in infos.deletedFiles)
			{
				var fileInfoPath = System.IO.Path.Combine(downloadPath, i.name);

				if (File.Exists(fileInfoPath))
				{
					try
					{
						File.Delete(fileInfoPath);
					}
					catch (Exception e)
					{
						LogHelper.Info("Update for delete file error, file:" + fileInfoPath + "exception：" + e.Message);
					}
				}
			}


		}

		static public void  DeleteUncheckedFile()
		{
			var infoPath = BaseInfoContentModel.Instance.GetValue("fileInfoJson", "");
			if (File.Exists(infoPath))
			{
				// 在新线程中执行CleanupUnlistedFiles
				Thread cleanupThread = new Thread(() => 
				{
					try
					{
						CleanupUnlistedFiles(infoPath);
					}
					catch (Exception ex)
					{
						LogHelper.Error($"Error in cleanup thread: {ex.Message}");
					}
				});
				cleanupThread.IsBackground = true; // 设置为后台线程，这样不会阻止应用程序退出
				cleanupThread.Start();
				LogHelper.Info("Started file cleanup in background thread");
			}
		}
		public override void Start(StateContext context)
		{
			Context.StateType = StateType.FileChecking;
			Thread t = new Thread(Check);
			t.Start();
		}
		public bool CheckDiff()
		{
			bool result = true;
			if (fileDiff == "")
				return result;
			LogHelper.Info("Enter File Check State for Check diff");
			if (File.Exists(fileDiff))
			{
				ReadCompareDiffListJson(fileDiff);
				// 检测下载区别
				result = CheckAddFile();
				result = result == false ? result : CheckDiffFile();
				DeleteRemoveFile();
			}
			return result;
		}

		private bool CheckFileInfo()
		{
			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var downloadPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			downloadPath = System.IO.Path.Combine(downloadPath, "IntoTheVoid");
			var fileInfoPath = BaseInfoContentModel.Instance.GetValue("fileInfoJson", "");

			if (!File.Exists(fileInfoPath))
			{
				LogHelper.Info($"CheckFile:{fileInfoPath} is not exist");
				return false;
			}

			List<Md5Core> md5List = ReadFileMd5ListJson(fileInfoPath);
			if (md5List.Count == 0)
			{
				LogHelper.Warn($"MD5 file List not exist:{fileInfoPath}");
				return false;
			}

			_totalFiles = md5List.Count;
			var errors = new ConcurrentBag<string>();

			// 配置并行选项
			var parallelOptions = new ParallelOptions
			{
				MaxDegreeOfParallelism = Environment.ProcessorCount * 2 // 根据测试调整
			};

			SetupProgressTimer();

			try
			{
				Parallel.ForEach(md5List, parallelOptions, md5Item =>
				{
					try
					{
						var filePath = System.IO.Path.Combine(downloadPath, md5Item.path);
						if (!File.Exists(filePath))
						{
							errors.Add($"MD5 file not exist:{filePath}");
							return;
						}

						var actualMd5 = Common.CaulculateFileMd5(filePath).ToLower();
						if (actualMd5 != md5Item.md5.ToLower())
						{
							errors.Add($"MD5 mismatch:{filePath} ({actualMd5} vs {md5Item.md5})");
						}

						Interlocked.Increment(ref _processedCount);
					}
					catch (Exception ex)
					{
						errors.Add($"Processing error:{ex.Message}");
					}
				});
			}
			finally
			{
				_progressTimer?.Stop();
				// 最终进度更新
				UpdateProgress(_totalFiles, _totalFiles);
			}

			foreach (var error in errors)
			{
				LogHelper.Warn(error);
			}

			return errors.IsEmpty;
		}

		private void SetupProgressTimer()
		{
			_processedCount = 0;
			_progressTimer = new System.Timers.Timer(50);
			_progressTimer.Elapsed += (sender, e) =>
			{
				try
				{
					var current = Interlocked.CompareExchange(ref _processedCount, 0, 0);
					UpdateProgress(current, _totalFiles);
				}
				catch (Exception ex)
				{
					LogHelper.Error($"Progress timer error: {ex.Message}");
				}
			};
			_progressTimer.Start();
		}

		private void UpdateProgress(int current, int total)
		{
			var progress = total > 0 ? (double)current / total * 100 : 0;
			Application.Current.Dispatcher.Invoke(() =>
			{
				Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(
					progress,
					$"资源校验中...[{current}/{total}]",
					"",
					""
				));
			});
		}
		private void Check()
		{
			Context.OnProcessStateArgs(new ProcessStateChangeEventArgs(0, "资源校验中...", "", ""));
			// 检查文件差异，删除多余文件
			bool result = CheckDiff();

			// 检查文件信息
			result = result == false ? result : CheckFileInfo();
			if (result == true)
			{
				Done(Context);
			}
			else
			{
				Context.OnTipsStateArgs(new TipsEventArgs("文件缺失，详细请看启动器日志，如果游戏存在异常情况，请去启动器设置界面手动选择\n <清理系统文件重新下载> \n重新下载游戏"));
				// 先不删了
				// Common.ClearDownloadFile();
				// Context.StateType = StateType.NeedDownload;
				// Context.CurState = new DownloadState(Context);
				Context.StateType = StateType.CanOpenGame;
				Context.CurState = new OpenGameState(Context);
			}
		}
		private bool CheckAddFile()
		{
			if (infos?.addedFiles == null) return true;

			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var downloadPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			downloadPath = System.IO.Path.Combine(downloadPath, "IntoTheVoid");

			var errors = new ConcurrentBag<string>();
			Parallel.ForEach(infos.addedFiles, new ParallelOptions { MaxDegreeOfParallelism = 8 }, info =>
			{
				var filePath = System.IO.Path.Combine(downloadPath, info.name);
				if (!File.Exists(filePath))
				{
					errors.Add($"Added file missing:{filePath}");
					return;
				}

				var actualMd5 = Common.CaulculateFileMd5(filePath).ToLower();
				if (actualMd5 != info.md5.ToLower())
				{
					errors.Add($"Added file MD5 mismatch:{filePath} ({actualMd5} vs {info.md5})");
				}
			});

			foreach (var error in errors) LogHelper.Warn(error);
			return errors.IsEmpty;
		}

		private void DeleteRemoveFile()
		{
			if (infos == null || infos.deletedFiles == null)
				return;
			// 删除remove文件列表文件
			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var downloadPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			downloadPath = System.IO.Path.Combine(downloadPath, "IntoTheVoid");
			foreach (var i in infos.deletedFiles)
			{
				var fileInfoPath = System.IO.Path.Combine(downloadPath, i.name);

				if (File.Exists(fileInfoPath))
				{
					try
					{
						File.Delete(fileInfoPath);
					}
					catch (Exception e)
					{
						LogHelper.Info("Update for delete file error, file:" + fileInfoPath + "exception：" + e.Message);
					}
				}
			}
			
			var infoPath = BaseInfoContentModel.Instance.GetValue("fileInfoJson", "");
			if (File.Exists(infoPath))
			{
				// 在新线程中执行CleanupUnlistedFiles
				Thread cleanupThread = new Thread(() => 
				{
					try
					{
						CleanupUnlistedFiles(infoPath);
					}
					catch (Exception ex)
					{
						LogHelper.Error($"Error in cleanup thread: {ex.Message}");
					}
				});
				cleanupThread.IsBackground = true; // 设置为后台线程，这样不会阻止应用程序退出
				cleanupThread.Start();
				LogHelper.Info("Started file cleanup in background thread");
			}
		}

		private bool CheckDiffFile()
		{
			if (infos?.updatedFiles == null) return true;

			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var downloadPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			downloadPath = System.IO.Path.Combine(downloadPath, "IntoTheVoid");

			var errors = new ConcurrentBag<string>();
			Parallel.ForEach(infos.updatedFiles, new ParallelOptions { MaxDegreeOfParallelism = 8 }, info =>
			{
				var filePath = System.IO.Path.Combine(downloadPath, info.name);
				if (!File.Exists(filePath))
				{
					errors.Add($"Updated file missing:{filePath}");
					return;
				}

				var actualMd5 = Common.CaulculateFileMd5(filePath).ToLower();
				if (actualMd5 != info.md5.ToLower())
				{
					errors.Add($"Updated file MD5 mismatch:{filePath} ({actualMd5} vs {info.md5})");
				}
			});

			foreach (var error in errors) LogHelper.Warn(error);
			return errors.IsEmpty;
		}
		public override void Done(StateContext download)
		{
			Context.OnTipsStateArgs(new TipsEventArgs("文件完整请开始游戏"));

			Context.StateType = StateType.CanOpenGame;
			Context.CurState = new OpenGameState(Context);

		}
		private bool GetJsonFile()
		{
			bool result = false;


			return result;
		}
		static public void ReadCompareDiffListJson(string filePath)
		{
			// 对比文件json
			using (StreamReader fileReader = new StreamReader(filePath))
			{
				string json = fileReader.ReadToEnd();
				try
				{
					infos = JsonConvert.DeserializeObject<UpdateDiffJsonFile>(json);
				}
				catch (Exception ex)
				{
					LogHelper.Error("ReadCompareDiffListJson Error:" + ex);
				}
			}
		}

		public List<Md5Core> ReadFileMd5ListJson(string filePath)
		{
			// 文件md5列表
			List<Md5Core> values = new List<Md5Core>();
			try
			{
				using (StreamReader fileReader = new StreamReader(filePath))
				{
					string json = fileReader.ReadToEnd();
					if (json != null)
					{
						dynamic infos = JsonConvert.DeserializeObject(json);
						foreach (var info in infos)
						{
							string name = info.Name;
							string md5 = info.Value;
							values.Add(new Md5Core(name, md5));
						}
					}
				}
			}
			catch (Exception ex)
			{
				LogHelper.Error("ReadFileMd5ListJson Error:" + ex);
			}
			return values;
		}
		
		/// <summary>
		/// 遍历文件夹中的所有文件，删除不在指定文件列表中的文件
		/// </summary>
		/// <param name="infoJsonPath">包含文件列表的JSON文件路径</param>
		/// <returns>返回是否成功完成操作</returns>
		static public bool CleanupUnlistedFiles(string infoJsonPath)
		{
			if (!File.Exists(infoJsonPath))
			{
				LogHelper.Info($"Info JSON file not found: {infoJsonPath}");
				return false;
			}
			
			// 获取游戏安装路径
			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var gamePath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			gamePath = System.IO.Path.Combine(gamePath, "IntoTheVoid");
			
			if (!Directory.Exists(gamePath))
			{
				LogHelper.Info($"Game directory not found: {gamePath}");
				return false;
			}
			
			// 读取文件列表JSON
			Dictionary<string, string> fileList = new Dictionary<string, string>();
			try
			{
				using (StreamReader fileReader = new StreamReader(infoJsonPath))
				{
					string json = fileReader.ReadToEnd();
					if (json != null)
					{
						dynamic infos = JsonConvert.DeserializeObject(json);
						foreach (var info in infos)
						{
							string name = info.Name;
							fileList[name] = ""; // 我们只需要文件名作为键，值不重要
						}
					}
				}
			}
			catch (Exception ex)
			{
				LogHelper.Error($"Error reading info JSON: {ex.Message}");
				return false;
			}
			
			if (fileList.Count == 0)
			{
				LogHelper.Warn("No files found in the info JSON");
				return false;
			}
			
			int deletedCount = 0;
			var errors = new ConcurrentBag<string>();
			
			// 递归遍历文件夹
			ProcessDirectory(gamePath, gamePath, fileList, ref deletedCount, errors);
			
			// 记录错误
			foreach (var error in errors)
			{
				LogHelper.Warn(error);
			}
			
			LogHelper.Info($"Cleanup completed. Deleted {deletedCount} unlisted files.");
			return true;
		}
		
		static private void ProcessDirectory(string rootPath, string currentPath, Dictionary<string, string> fileList, ref int deletedCount, ConcurrentBag<string> errors)
		{
			try
			{
				// 处理当前目录中的文件
				foreach (var filePath in Directory.GetFiles(currentPath))
				{
					try
					{
						// 获取相对于游戏根目录的路径
						string relativePath = filePath.Substring(rootPath.Length).TrimStart('\\', '/');
						
						// 跳过JSON文件的检查
						if (System.IO.Path.GetExtension(filePath).ToLower() == ".json")
						{
							LogHelper.Info($"Skipping JSON file: {relativePath}");
							continue;
						}
						
						// 检查文件是否在列表中
						if (!fileList.ContainsKey(relativePath))
						{
							try
							{
								File.Delete(filePath);
								Interlocked.Increment(ref deletedCount);
								LogHelper.Info($"Deleted unlisted file: {relativePath}");
							}
							catch (Exception ex)
							{
								errors.Add($"Failed to delete file {relativePath}: {ex.Message}");
							}
						}
					}
					catch (Exception ex)
					{
						errors.Add($"Error processing file {filePath}: {ex.Message}");
					}
				}
				
				// 递归处理子目录
				foreach (var dirPath in Directory.GetDirectories(currentPath))
				{
					ProcessDirectory(rootPath, dirPath, fileList, ref deletedCount, errors);
				}
			}
			catch (Exception ex)
			{
				errors.Add($"Error accessing directory {currentPath}: {ex.Message}");
			}
		}
	}
}
